﻿using GSPatEditor.Pat;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GSPatEditor.Simulator
{
    class World
    {
        private CollisionDetector collision;

        //private int Left, Top, Width, Height;

        private List<Actor> actorList = new List<Actor>();
        private List<Actor> toAddActor = new List<Actor>();
        private List<Actor> toRemoveActor = new List<Actor>();

        private bool actorListDirty = false;

        public readonly Random Random = new Random();

        public World(int left, int top, int width, int height)
        {
            this.collision = new CollisionDetector(left, top, width, height);
        }

        public void CreateActor(Action<Actor> setupFunction, float x, float y)
        {
            Actor actor = new Actor(this);
            setupFunction(actor);
            actor.X = x;
            actor.Y = y;
            this.AddActor(actor);
        }

        public void AddActor(Actor actor)
        {
            toAddActor.Add(actor);
            actorListDirty = true;

            if (!actor.IsInAir)
            {
                collision.PutActorOnGround(actor);
            }
        }

        public void RemoveActor(Actor actor)
        {
            toRemoveActor.Add(actor);
        }

        public void Update()
        {
            foreach (var actor in actorList)
            {
                actor.Update();

                if (actor.IsReleased)
                {
                    continue;
                }

                collision.TestActor(actor);
                if (actor.IsInAir && collision.HitGround)
                {
                    if (actor.SitLabel != null)
                    {
                        actor.SitLabel(actor);
                    }
                }
                actor.IsInAir = !collision.HitGround;
            }
            foreach (var actor in toAddActor)
            {
                actorList.Add(actor);
            }
            toAddActor.Clear();
            foreach (var actor in toRemoveActor)
            {
                actorList.Remove(actor);
            }
            toRemoveActor.Clear();
        }

        public void Render(Graphics g, AnimationImageCache cache)
        {
            //first sort
            if (actorListDirty)
            {
                actorList.Sort((Actor a, Actor b) => a.Priority.CompareTo(b.Priority));
            }

            g.InterpolationMode = InterpolationMode.Default;
            g.PixelOffsetMode = PixelOffsetMode.Half;
            var mat = g.Transform;
            foreach (var actor in actorList)
            {
                g.Transform = mat;

                var frame = actor.CurrentFrame;

                //actor position
                g.TranslateTransform((float)Math.Floor(actor.X), (float)Math.Floor(actor.Y));

                //TODO rotation in ImageTransformation
                float rotate = actor.Rotation / (float)Math.PI * 180;
                if (frame.ImageTransformation != null)
                {
                    rotate += frame.ImageTransformation.Rotate * 1.0f;
                }
                g.RotateTransform(rotate);

                //scale
                float scaleX = actor.ScaleX, scaleY = actor.ScaleY;
                if (frame.ImageTransformation != null)
                {
                    scaleX *= frame.ImageTransformation.Scale.X / 100.0f;
                    scaleY *= frame.ImageTransformation.Scale.Y / 100.0f;
                }
                g.ScaleTransform(scaleX, scaleY);

                //origin
                g.TranslateTransform(-frame.Origin.X, -frame.Origin.Y);

                g.DrawImage(cache.GetBitmapForAnimation(actor.CurrentAnimation, actor.CurrentFrameIndex),
                    new PointF(0.0f, 0.0f));
            }
        }
    }
}
